package tr.gov.tubitak.uekae.applet;

import netscape.javascript.JSException;
import netscape.javascript.JSObject;
import org.apache.log4j.PropertyConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tr.gov.tubitak.uekae.applet.ops.ContainerOps;
import tr.gov.tubitak.uekae.applet.ops.Ops;
import tr.gov.tubitak.uekae.applet.resource.ResourceUtil;
import tr.gov.tubitak.uekae.esya.api.asn.x509.ECertificate;
import tr.gov.tubitak.uekae.esya.api.common.crypto.Algorithms;
import tr.gov.tubitak.uekae.esya.api.common.crypto.BaseSigner;
import tr.gov.tubitak.uekae.esya.api.common.util.LicenseUtil;
import tr.gov.tubitak.uekae.esya.api.signature.SignatureFormat;
import tr.gov.tubitak.uekae.esya.api.smartcard.pkcs11.BaseSmartCard;
import tr.gov.tubitak.uekae.esya.api.smartcard.pkcs11.SmartCardException;

import java.applet.Applet;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;

/**
 * Main class of applet. This class runs first.
 * @author: suleyman.uslu
 */
public class WebLoginApplet extends Applet {

    private static Logger msLogger = LoggerFactory.getLogger(WebLoginApplet.class);
    SmartCardManager scManager;
    public String mLoginFormCerts;
    int mTerminalNo;
    String mTerminal;
    ECertificate mCert;
    BaseSigner mBaseSigner;
    //String mLoginFormTerminals;

    enum SignatureType {/*XAdES, CAdES,*/ Container}

    SignatureType signatureType = SignatureType.Container;

    // signature format of conatiner
    SignatureFormat signatureFormat = SignatureFormat.XAdES;

    @Override
    public void start() {
    }

    /**
     * Initialization function of the applet
     */
    public void init() {

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

            //public Object run() {

                parameterInit();
                loginInit();
                msLogger.debug("initialization successful :)");
                //return null;
            //}
        //});
    }



    /**
     * Initializes required parameters
     */
    private void parameterInit() {

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

            //public Object run() {

                try {
                    PropertyConfigurator.configure(new FileInputStream(ResourceUtil.log4jPath));
                    msLogger.debug("Log4j is used. Configuration path is " + ResourceUtil.log4jPath);

                    LicenseUtil.setLicenseXml(new FileInputStream(ResourceUtil.licensePath));
                    msLogger.debug("License is being loaded from " + ResourceUtil.licensePath);
                }
                catch (Exception e) {
                    msLogger.debug(e.getMessage(), e);
                }

                //return null;
            //}
        //});
    }

    /**
     * Finds the certificates in smart cards and passes to
     * Javascript for certificates to be printed in the HTML form
     */
    private void loginInit()/* throws PrivilegedActionException */{

        final JSObject jsObject = JSObject.getWindow(this);

        //AccessController.doPrivileged(new PrivilegedAction<Object>() {

            //public Object run() {

                String loginFormCerts = "";

                try {

                    scManager = SmartCardManager.getInstance();
                    scManager.listCertificatesOfTerminals();

                    //loginFormCerts = loginFormCerts.concat("<option value=\"NoCert\">Please Select Certificate</option>");
                    loginFormCerts = loginFormCerts.concat("<table>");
                    loginFormCerts = loginFormCerts.concat("<tr><th></th><th>Please your certificate</th></tr>");

                    for(ECertificate cert : scManager.mSignatureCerts) {

                        String certName = cert.getSubject().getCommonNameAttribute();
                        String issuer = cert.getIssuer().getCommonNameAttribute();
                        String certSerialHex = cert.getSerialNumberHex();

                        //loginFormCerts = loginFormCerts.concat("<option value=\""+certSerialHex+"\">"+certName+"</option>");
                        loginFormCerts = loginFormCerts.concat("<tr><td><input type='radio' name='certRadios' value='"+certSerialHex+"' /></td><td>"+certName+"</td></tr>");
                        loginFormCerts = loginFormCerts.concat("<tr><td></td><td>"+issuer+"</td></tr>");
                    }
                    loginFormCerts = loginFormCerts.concat("</table>");

                } catch (SmartCardException e) {
                    e.printStackTrace();
                }

                //if(mLoginFormCerts == null || mLoginFormCerts == "")
                mLoginFormCerts = loginFormCerts;

                // call javascript function to print certificates of connected smart cards to login page
                jsObject.call("setLoginFormCerts", new Object[]{mLoginFormCerts});

                //return null;
            //}
        //});
    }

    /**
     * Called from home page and signs given document with signer that applet holds
     * @param doc    document to be signed
     */
    public void signDocument(String doc) {

        JSObject jsObject = JSObject.getWindow(this);

        msLogger.debug("Document is being signed in home page");

        Ops ops = null;

        switch(signatureType) {
            //case XAdES : ops = new XAdESOps(doc.getBytes(), mCert, mBaseSigner);
            //case CAdES : ops = new CAdESOps(doc.getBytes(), mCert, mBaseSigner);
            case Container : ops = new ContainerOps(doc.getBytes(), mCert, mBaseSigner, signatureFormat);
        }

        msLogger.debug("Document is signed successfully");
        String signature64 = ops.sign();//Base64.encode(signature);

        // Prints signature in base64 format to HTML
        jsObject.call("setSignature64", new Object[]{signature64});
    }

    /**
     * Called when logout button is clicked and logs out from smart card
     * @throws SmartCardException
     */
    public void logout() throws SmartCardException {

        msLogger.debug("logout from applet is called");
        try {
            // logout from smart card
            BaseSmartCard bsc = scManager.smartCards.get(mTerminalNo);
            bsc.logout();
            msLogger.debug("logged out");
        }
        catch (Exception e) {
            msLogger.debug("error in logging out and closing session", e);
        }
    }

    /**
     * Signs the session id, called from login.jsp
     * @param doc document to be signed
     * @param certHex certificate serial no in hex form
     * @param pin pin of smart card
     */
    public void signFromHTML(final String doc, final String certHex, final String pin) {

        final JSObject jsObject = JSObject.getWindow(this);
        msLogger.debug("Signing process for login has started");

        // jar imzali da olsa yetkilerde sorun cikiyordu AccessController olmadan
        //AccessController.doPrivileged(new PrivilegedAction() {

            //public Object run() {

                int certNo = -1;
                for(ECertificate cert : scManager.mSignatureCerts) {

                    certNo++;

                    if(certHex.equals(cert.getSerialNumberHex())) {

                        try {
                            msLogger.debug("matching certificate found " + certHex + " " + cert.getSerialNumberHex());
                            byte[] docBytes = doc.getBytes("UTF-8");

                            mCert = cert;
                            msLogger.debug("Certificate No : " + certNo);

                            String terminal = scManager.certTerminalMap.get(certNo);
                            mTerminal = terminal;
                            msLogger.debug("Terminal : " + terminal);

                            int terminalNo = scManager.mTerminalList.indexOf(terminal);
                            mTerminalNo = terminalNo;
                            msLogger.debug("Terminal No : " + terminalNo);

                            BaseSmartCard baseSmartCard = scManager.smartCards.get(terminalNo);

                            // session acmaktan vazgectim, sertifika listesini okurken actigim sessionlari
                            // acik birakiyorum, boylece yeniden session acmak icin zaman harcamiyorum
                            // kullanilmayacak kartlardaki sessionlari kapatiyorum, imza attiktan sonra
                            // bizim karttaki session'i da kapatiyorum, dezavantaji session acik kalabiliyor
                            // islem yapilmazsa, diger bir yontem ise session'lari acik birakmak

                            // imza icin kullanilacak terminal haric diger terminal session'larini kapat
                            /*for (String term : scManager.mTerminalList) {
                                if (!term.equals(terminal)) {
                                    int termNo = scManager.mTerminalList.indexOf(term);
                                    scManager.smartCards.get(termNo).closeSession();
                                }
                            }*/

                            msLogger.debug("Logging in to smart card");
                            try {
                                baseSmartCard.login(pin);
                            } catch (Exception e) {
                                msLogger.debug("Error in login to smart card", e);
                                e.printStackTrace();
                                msLogger.debug(e.getMessage());
                            }

                            BaseSigner baseSigner = baseSmartCard.getSigner(cert.asX509Certificate(), Algorithms.SIGNATURE_RSA_SHA256);

                            mBaseSigner = baseSigner;

                            Ops ops = null;

                            switch(signatureType) {
                                //case XAdES : ops = new XAdESOps(doc.getBytes(), mCert, mBaseSigner);
                                //case CAdES : ops = new CAdESOps(doc.getBytes(), mCert, mBaseSigner);
                                case Container : ops = new ContainerOps(doc.getBytes(), mCert, mBaseSigner, signatureFormat);
                            }

                            msLogger.debug("Document is signed successfully");
                            String signature64 = ops.sign();//Base64.encode(signature);


                            // close terminal session
                            /*msLogger.debug("Closing session");
                            baseSmartCard.closeSession();
                            if(!baseSmartCard.isSessionActive())
                                msLogger.debug("Session is successfully closed");
                            else
                                msLogger.debug("Error in session closing");*/

                            // Print signature to HTML in base 64 format
                            jsObject.call("setSignature64", new Object[]{signature64});

                        } catch (UnsupportedEncodingException e) {
                            msLogger.debug(e.getMessage(), e);
                        } catch (JSException e) {
                            msLogger.debug(e.getMessage(), e);
                        } catch (SmartCardException e) {
                            msLogger.debug(e.getMessage(), e);
                        }
                    }
                }

            //    return null;
            //}
        //});
    }

    public static void main(String[]args) {

    }

}
